マルチスレッド                                

■ スレッド作成、書込み

 

     @Thread t1 = new Thread( )で Threadクラスのオブジェクトを作成する。   
  ・コンストラクタのパラメータにThreadStartクラスのオブジェクトを指定
  ・ThreadStartクラスのコンストラクタには、デリゲートInvokeメソッドを指定する
Aデリゲートを宣言する
B実際に実行するメソッドを作成する
CデリゲートInvokeメソッドでデリゲートに実際に実行するメソッドを実装する
 上記の4項目がスレッド生成・生成スレッドからの書込みをする場合必要となる

下記のプログラムは以下の仕様である。
  @ button1をクリックすると起動スレッドからlabel1に現在時刻を書き込む
  A button2をクリックすると生成した別スレッドからlabel1に現在時刻を書き込む

実行結果
   

          

   <プログラム例>
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading;


namespace WindowsFormsmultithread
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        //デリゲート宣言する
        delegate void MyDelegate();

        void DisplayTime()
        {
            //現在の時刻を表示する
            label1.Text = DateTime.Now.ToString();
        }

        void DisplayTime2()
        {
            label1.Text = DateTime.Now.ToString();
        }

        public void myInvoke()  //デリゲートInvokeメソッド
        {
              //Invokeが必要な時はInvokeを使い、不必要の時は直接書き込む
            if (InvokeRequired)
                Invoke(new MyDelegate(DisplayTime2));   //デリゲートにメソッドを実装
            else
                DisplayTime();
        }




        private void button1_Click(object sender, EventArgs e) //Form1スレッドから表示
        {
                myInvoke();
        }

        private void button2_Click(object sender, EventArgs e) //第2スレッドから表示
        {
            Thread t1 = new Thread(new ThreadStart(myInvoke));//Threadクラスのインスタンスを作成する
            //★ コンストラクタのパラメータにThreadStartクラスのオブジェクトを指定する。
            //★ ThreadStartクラスのコンストラクタには、デリゲートInvokeメソッドを指定する
            t1.Start(); //スレッド実行
                        
        }

      
    }


}









 バックグランドワーカによる別スレッド処理の進捗表示

  @BackgroundWorkerコンポーネントをフォームにドラッグする
AackgroundWorker1.RunWorkerAsync( )メソッドでバックグランドワーカの別スレッドを起動させる。
BbackgroundWorker1_DoWork( ) イベントハンドラで別スレッドからのレポートを受ける
CBackgroundWorker1_RunWorkerCompleted( )イベントハンドラで別スレッドの処理終了レポートを受ける
Dキャンセルが発生していないか別スレッドで定期的にチェックする。キャンセルの発生有無はBackgroundWorker1_RunWorkerCompleted( )イベントハンドラで
 常時チェックする
実行結果 
 
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace BackGroundWoker_Progress
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
CancelBtn.Enabled = false;
backgroundWorker1.WorkerReportsProgress = true; //進捗報告許可 // backgroundWorker1.ReportProgress ではない
backgroundWorker1.WorkerSupportsCancellation = true; //キャンセルの受付許可 //backgroundWorker1.CancellationPendingではない
}
private void StartBtn_Click(object sender, EventArgs e) // スタートボタンのイベント・ハンドラ
{
StartBtn.Enabled = false;
CancelBtn.Enabled = true;
backgroundWorker1.RunWorkerAsync(100); // DoWorkイベント発生 // 時間のかかる処理を別スレッドで開始
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) // 時間のかかる処理を行うメソッド
{

int bgWorkerArg = (int)e.Argument; // このメソッドへのパラメータを取得

BackgroundWorker worker = (BackgroundWorker)sender; // senderの値はbackgrondWorker1の値と同じ

// 時間のかかる処理
for (int i = 0; i < bgWorkerArg; i++)
{
// キャンセルされてないか定期的にチェック
if(worker.CancellationPending == true)
{
e.Cancel = true; //DoWorkイベント・ハンドラを抜ける
return;
}

System.Threading.Thread.Sleep(100); //100msec停止しているスレッドはこのbackgroundWorker1のスレッドだけである。
//Form1上のUIコンポーネントは動作可中である。

int percentage = i * 100 / bgWorkerArg; // 進ちょく率
worker.ReportProgress(percentage); // ProgressChangedイベント発生
}
e.Result = "すべて完了"; // この後、RunWorkerCompletedイベントが発生 // このメソッドからの戻り値
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e) //
{
// 進捗状況の表示
this.Text = e.ProgressPercentage + "%完了";
progressBar1.Value = e.ProgressPercentage; // = percentage1
}
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Cancelled)//キャンセルの場合
{
MessageBox.Show("キャンセルされました"); // この場合にはe.Resultにはアクセスできない
}
else
{
this.Text = e.Result.ToString(); // 処理結果の表示
MessageBox.Show("正常に完了");
}
StartBtn.Enabled = true;
CancelBtn.Enabled = false;
}
private void CancelBtn_Click(object sender, EventArgs e) //キャンセルボタンのイベントハンドラ
{
// 時間のかかる処理のキャンセル
backgroundWorker1.CancelAsync();
}
}
}

<起動時>



<バックグランドスレッド動作中>



<バックグランスレッド処理 正常終了後>



<キャンセル発生後>


■ 別スレッドから フォーム上のコントロールを操作(ランプ フリーラン 

   マルチスレッド制御には、いくつかあるようですが 単に2〜3個の別スレッドから速度をあまり問題とせずメインフォームのコントロールを
操作するだけならINVOKEでデリゲートを呼び出す方法が一番簡単です。 Win32APIのセフォマやミューテックス関数を使った排他制御は結構大変です。
どのように排他制御、メモリ保護が行われているかといったことは不問にして マイクロソフトが用意してくれたデリゲート(意味:代表、代理人)にINVOKE
(意味:祈願する、お願いする)するのが賢明です。
使い方はいたって簡単です。

@デリゲートの型を宣言する  delegate (戻り値) (デリゲート型名)(引数1,引数2,.........);  (例)   delegate void MyDelegate(bool flag,);
A呼び出す関数を定義する。
  → この関数の中に 実際に操作したいフォームのコントロールのメソッドを書くことができる。
  private void ContFunc(bool flag) //関数を定義
  {
  ...
.  ..
  }
B別スレッドメソッドの中に デリゲートのインスタンスを生成し、呼び出し関数も含め定義する。
  MyDelegate mydelegate = new MyDelegate(ContFunc);
C関数をINVOKEメソッドで呼び出す
  this.Invoke(mydelegate, Flag); //関数の呼び出し

 以下は、バックグラウンドワーカの別スレッドから メインフレームのラベルとピクチャーボックスを操作しています。  backgroundWorker1_DoWork( )の
中は while(true)でエンドレスになっています。 スリープ1秒 x 2個を繰り返して デリゲートによりラベルとピクチャーボックスを1秒ごとに書き換えています。
 
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;



namespace LampFreeRun_by_bgWoker
{
public partial class Form1 : Form
{
bool LED = false;
public Form1()
{
InitializeComponent();

OnOffLbl.Text = "OFF";
StartBtn.Enabled = true;
StopBtn.Enabled = false;

pictureBox1.Image = new Bitmap(pictureBox1.Width, pictureBox1.Height); //pictureBox1のイメージデータのオブジェクト生成
Graphics g = Graphics.FromImage(pictureBox1.Image); //イメージからグラフィックスを作成
g.FillEllipse(Brushes.Gray, 0, 0, 50, 50); //灰色塗りつぶし

backgroundWorker1.WorkerSupportsCancellation = true; //バックグランドワーカのキャンセル許可
}

private void StartBtn_Click(object sender, EventArgs e) //スタートボタン
{
backgroundWorker1.RunWorkerAsync();
StartBtn.Enabled = false;
StopBtn.Enabled = true;
}

private void StopBtn_Click(object sender, EventArgs e) //ストップボタン
{
backgroundWorker1.CancelAsync();
StartBtn.Enabled = true;
StopBtn.Enabled = false;
}

delegate void MyDelegate(bool flag);//デリゲートの型を宣言
//void: 戻り値なし  引数:bool型 x1


private void ContFunc(bool flag) //関数を定義   //void: 戻り値なし  引数:bool型 x1個
{
pictureBox1.Image = new Bitmap(pictureBox1.Width, pictureBox1.Height); //pictureBox1のイメージデータのオブジェクト生成
Graphics g = Graphics.FromImage(pictureBox1.Image); //イメージからグラフィックスを作成
if (flag == true)
{
OnOffLbl.Text = "ON";
g.FillEllipse(Brushes.Orange, 0, 0, 50, 50); //灰色塗りつぶし }
else
{
OnOffLbl.Text = "OFF";
g.FillEllipse(Brushes.Gray, 0, 0, 50, 50); //灰色塗りつぶし
}
}


private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
bool Flag;

while (true)
{

if (LED == false)
{
if (backgroundWorker1.CancellationPending == true)
{
e.Cancel = true; //DoWorkイベント・ハンドラを抜ける
return;
}
LED = true;
System.Threading.Thread.Sleep(1000);
}
else
{
if (backgroundWorker1.CancellationPending == true)
{
e.Cancel = true; //DoWorkイベント・ハンドラを抜ける
return;
}
LED = false;
System.Threading.Thread.Sleep(1000);
}
Flag = LED;

MyDelegate mydelegate = new MyDelegate(ContFunc);//関数ContFunc()を実装したデリゲートのオブジェクトを生成
this.Invoke(mydelegate, Flag); //関数の呼び出し


} // while (true)
}
}
}
   <実行結果>
 
<ランプ オフの場合>



<ランプ オンの場合>